#include "sct/rtspclient/rtspclient.h"
#define RTSP_VER "1.0.10"

namespace sct
{
void CRTSPClient::setSDP(const std::string & sdp)
{
	mSdp = sdp;
}

void CRTSPClient::parseAACParam(const char* sdp, CAVParam * param)
{
	param->channel_config = -1;
	param->frequency = -1;
	param->profile = -1;

	char * s = NULL;

	s = (char*)sdp;
	s = strstr(s, "MPEG4-GENERIC");
	if(s)
	{
		s = strstr(s, "/");
		if(s)
		{
			s += 1;
			sscanf(s, "%d", &param->frequency);

			s = strstr(s, "/");
			if(s)
			{
				s += 1;
				sscanf(s, "%d", &param->channel_config);
			}
		}
	}

	s = (char*)sdp;
	s = strstr(s, "MPEG4-GENERIC");
	if(s)
	{
		s = strstr(s, "profile-level-id");
		if(s)
		{
			s = strstr(s, "=");
			if(s)
			{
				s += 1;

				sscanf(s, "%d", &param->profile);
			}
		}
	}
}

void CRTSPClient::continueAfterDESCRIBE( RTSPClient* client, int result_code,
                                   char* result_string )
{
	CMyRTSPClient *p = static_cast<CMyRTSPClient *> ( client );

	if(0 == result_code)
	{
		p->mOwner->mErr = false;
		p->mOwner->setSDP(result_string);
	}
	else
	{
		//make err flag
		p->mOwner->mErr = true;
	}

	SCT_LOGD("describe:%s %d", result_string, result_code);

	delete[] result_string;
	p->mOwner->mEventFlag = 1;
}

void CRTSPClient::continueAfterGetParameter(
			RTSPClient* client,
			int result_code,
			char* result_string )
{
	CMyRTSPClient * p = dynamic_cast<CMyRTSPClient *>(client);
	delete[]result_string;

	if(454 == result_code)
	{
		p->mOwner->mNeedReset = 1;
		SCT_LOGD("GetParameter failed");
	}
}

void CRTSPClient::continueAfterOPTIONS( RTSPClient* client, int result_code,
                                  char* result_string )
{
	CMyRTSPClient * p = dynamic_cast<CMyRTSPClient *>(client);

	if(0 == result_code)
	{
		p->sendDescribeCommand( continueAfterDESCRIBE );

		if(strstr(result_string, "GET_PARAMETER") != NULL)
		{
			p->mOwner->mKeepAlive = 1;
		}
	}
	else
	{
		//make err flag.
		p->mOwner->mErr = true;
	}

	SCT_LOGD("options:%s %d", result_string, result_code);

    delete[] result_string;
}

BasicTaskScheduler * CRTSPClient::scheduler()
{
	return mSched;
}

CMyRTSPClient * CRTSPClient::liveClient()
{
	return mMyClient;
}

void CRTSPClient::TaskInterruptRTSP( void *p_private )
{
	CRTSPClient * pClient = (CRTSPClient*)p_private;

	pClient->mEventFlag = -1;
}

int CRTSPClient::intervalKeepAlive()const
{
	return mIntervalKeepAlive;
}

MediaSession * CRTSPClient::mediaSession()
{
	return mMs;
}

void CRTSPClient::TaskKeepAlive( void *p_private )
{
	CRTSPClient * pClient = (CRTSPClient*)p_private;

	if(pClient->mMyClient)
	{
		pClient->mMyClient->sendGetParameterCommand(*pClient->mMs, continueAfterGetParameter, NULL);
	}
	else
	{
		SCT_LOGD("client reset. cancel keep alive ");
	}

	if(pClient->mTaskKeepAlive)
	{
		pClient->mSched->unscheduleDelayedTask(pClient->mTaskKeepAlive);
		pClient->mTaskKeepAlive = NULL;
	}

	pClient->mTaskKeepAlive = pClient->mSched->scheduleDelayedTask(
			pClient->mIntervalKeepAlive*1000,
			TaskKeepAlive,
	        pClient );
}

bool CRTSPClient::wait_Live555_response(int i_timeout)
{
    TaskToken task;

    if( i_timeout > 0 )
    {
        /* Create a task that will be called if we wait more than timeout ms */
        task = mSched->scheduleDelayedTask( i_timeout*1000,
                                                      TaskInterruptRTSP,
                                                      this );
    }

    mEventFlag = 0;
    mErr = true;

    mSched->doEventLoop( &mEventFlag );
    //here, if b_error is true and i_live555_ret = 0 we didn't receive a response
    if( i_timeout > 0 && -1 != mEventFlag)
    {
        /* remove the task */
    	mSched->unscheduleDelayedTask( task );
    }
    return !mErr;
}

void CRTSPClient::default_live555_callback( RTSPClient* client, int result_code, char* result_string )
{
	CMyRTSPClient *p = static_cast<CMyRTSPClient *> ( client );

	SCT_LOGD("reponse:%s", result_string);

    delete []result_string;
    p->mOwner->mRetCode = result_code;
    p->mOwner->mErr = p->mOwner->mRetCode != 0;
    p->mOwner->mEventFlag = 1;
}
void CRTSPClient::reset()
{
	CAutoLock locker(mObjMutex);
	_reset();
}


void CRTSPClient::_reset()
{

	if(mTaskKeepAlive)
	{
		mSched->unscheduleDelayedTask(mTaskKeepAlive);
		mTaskKeepAlive=NULL;
	}

	if(mMyClient) mMyClient->sendTeardownCommand(*mMs, NULL);
	RTSPClient::close(mMyClient);mMyClient=NULL;
	if(mMs)
	{
		Medium::close(mMs);mMs = NULL;

		std::list<CTrack*>::iterator it;
		for(it = mTracks.begin(); it != mTracks.end(); it++)
		{
			delete[] (*it)->buffer;
			delete *it;
		}

		mTracks.clear();
	}

	///slow down
	SCT_LOGD("after %d ms, restart rtsp...", mMaxTimeOutLimit);
	usleep(mMaxTimeOutLimit*1000);
	mState = S_Init;

}

void CRTSPClient::start()
{
	SCT_ASSERT(NULL == mMyClient);
	mMyClient = new CMyRTSPClient(this, *mEnv, mTargetServerUrl.c_str()
			//,3
			);


	mKeepAlive = 0;
	mNeedReset = 0;
	//connect option
	mErr = true;
	mMyClient->sendOptionsCommand(&continueAfterOPTIONS);

	///fixme: 500 is ok?
	if(!wait_Live555_response( mMaxTimeOutLimit ))
	{
		//no reponse
		mState = S_Terminate;
	}
	else
	{
		//session setup
		mMs = MediaSession::createNew(*mEnv, mSdp.c_str());

		MediaSubsessionIterator *iter   = NULL;
		MediaSubsession         *sub    = NULL;

		unsigned int   i_receive_buffer = 2000000;
		int            i_frame_buffer = 500000;
		//unsigned const thresh = 300000;
		//unsigned const thresh = 200000;
		unsigned const thresh = 0;

		SCT_LOGD("thresh time:%u", thresh);

		iter = new MediaSubsessionIterator( *mMs );
		while( ( sub = iter->next() ) != NULL )
		{
			Boolean bInit;

			bInit = sub->initiate();
			if(bInit)
			{
				if( sub->rtpSource() != NULL )
				{
					int fd = sub->rtpSource()->RTPgs()->socketNum();

					/* Increase the buffer size */
					int sz = increaseReceiveBufferTo( *mEnv, fd, i_receive_buffer );

					SCT_LOGD("recv buffer:%d",sz);

					/* Increase the RTP reorder timebuffer just a bit */
					sub->rtpSource()->setPacketReorderingThresholdTime(thresh);
				}

				mMyClient->sendSetupCommand( *sub, default_live555_callback);
				if( !wait_Live555_response( mMaxTimeOutLimit ) )
				{
					//no reponse
					continue;
				}

				if( sub->readSource() == NULL ) continue;

				CTrack * track = new CTrack;
				track->sub = sub;
				track->sz_buffer = i_frame_buffer;
				track->buffer = new uint8_t[track->sz_buffer];
				track->owner = this;

				parseAACParam(mSdp.c_str(), &track->av_param);

				mTracks.push_back(track);
			}
		}
		//play
		mMyClient->sendPlayCommand( *mMs, default_live555_callback);

		if( !wait_Live555_response(mMaxTimeOutLimit) )
		{
			//no reponse
		}

		if(mKeepAlive)
		{
			TaskKeepAlive(this);
		}
		mState = S_Play;
		mLastGetDataTime = currentTime();
	}
}

CRTSPClient::CRTSPClient()
{
	SCT_LOGD("RTSP client:Ver:%s", RTSP_VER);

	mSched = BasicTaskScheduler::createNew();
	mEnv = BasicUsageEnvironment::createNew(*mSched);

	*mEnv << "test live555 BasicUsageEnvironment";

	mMyClient = NULL;
	mMs = NULL;
	mState = 0;

	mState = S_Init;
	SCT_LOGD();

	mMaxTimeOutLimit = 500;
	mIntervalKeepAlive = 1000;
	mEventFlag = 0;
	mErr = false;
	mRetCode = 0;

	mKeepAlive = 0;
	mTaskKeepAlive = NULL;
	mNeedReset = 1;

	mLastGetDataTime = 0;
	mMaxGapTime = 3;


	mMaxDelay = 3000000;

	mObjMutex = new CMutex;

	mServerUrl = mTargetServerUrl = "rtsp://192.168.100.1/live";

}

void CRTSPClient::setMaxDelay(const int64_t delay)
{
	CAutoLock locker(mObjMutex);

	_setMaxDelay(delay);
}

void CRTSPClient::_setMaxDelay(const int64_t delay)
{
	_reset();
	mMaxDelay = delay;
}


void CRTSPClient::StreamRead( void *p_private, unsigned int i_size,
                        unsigned int i_truncated_bytes, struct timeval pts,
                        unsigned int duration )
{
	CRTSPClient::CTrack* track = (CRTSPClient::CTrack*)p_private;
	track->owner->mEventFlag = 1;
	track->owner->mLastGetDataTime = currentTime();

	if(!track->sub->rtpSource()->hasBeenSynchronizedUsingRTCP())
	{
		return;
	}

	int64_t ipts = pts.tv_sec;
	ipts *= 1000000;
	ipts += pts.tv_usec;

	///fix live555 pts can't accurate to us error. mini unit is ms.
	ipts >>= 10;
	ipts <<= 10;

	uint32_t dropFramePre = 0;
	memcpy(&dropFramePre, (uint8_t*)track->buffer + i_size, 4);


	if(!strcmp(track->sub->codecName(), "MP2T"))
	{
		track->owner->pushTsData(ipts, (uint8_t*)track->buffer, i_size, dropFramePre);
	}
	else if(!strcmp(track->sub->codecName(), "H264"))
	{
		track->owner->pushH264Data(ipts, (uint8_t*)track->buffer, i_size, dropFramePre);
	}
	else if(!strcmp(track->sub->codecName(), "MPEG4-GENERIC"))
	{
		track->owner->pushAACData(track, ipts, (uint8_t*)track->buffer, i_size, dropFramePre);
	}
	else if(!strcmp(track->sub->codecName(), "AMR"))
	{
		track->owner->pushAMRData(ipts, (uint8_t*)track->buffer, i_size, dropFramePre);
	}
	else
	{
		SCT_LOGD("codec name:%s", track->sub->codecName());
	}

	///if the buffer too small will should make it bigger.
	if(i_truncated_bytes > 0)
	{
		int sz = i_truncated_bytes + track->sz_buffer;
		if(sz < 5000000)
		{
			SCT_DELETE_ARRAY(track->buffer);

			track->sz_buffer = sz;
			track->buffer = new uint8_t[track->sz_buffer];

			SCT_LOGD("buffer increase to %d byte", track->sz_buffer);
		}
		else
		{
			SCT_LOGD("buffer size increase to 5MB. is right?");
		}
	}

}

void CRTSPClient::StreamClose( void *p_private )
{
	SCT_LOGD("stream close");
}

void CRTSPClient::TaskInterruptData( void *p_private )
{
	CRTSPClient *p = (CRTSPClient*)p_private;


    p->mEventFlag = -1;

}
void CRTSPClient::checkIfNeedReset()
{
	if(S_Play == mState )
	{
		double cur = currentTime();
		cur -= mLastGetDataTime;

		if(cur > mMaxGapTime)
		{
			SCT_LOGD("no data recv from server. reconnect...");
			mNeedReset = 1;
		}
	}

	if(mTargetServerUrl != mServerUrl)
	{
		SCT_LOGD("reset to set url:%s--->%s", mServerUrl.c_str(), mTargetServerUrl.c_str());
		mServerUrl = mTargetServerUrl;///assume reset will work ok.
		mNeedReset = 1;
	}
}

void CRTSPClient::setServerUrl(const char * url)
{
	CAutoLock locker(mObjMutex);

	mTargetServerUrl = url;

	SCT_LOGD("url:%s %s", mTargetServerUrl.c_str(), url);
}

int CRTSPClient::demux()
{
	CAutoLock locker(mObjMutex);

	///do the state trans
	checkIfNeedReset();
	if(mNeedReset)
	{
		mState = S_Terminate;
		mNeedReset = 0;
	}
	if(S_Play != mState)
	{
		if(S_Init == mState)
		{
			start();
		}
		if(S_Terminate == mState )
		{
			_reset();
		}
		return -1;
	}

	std::list<CTrack*>::iterator it;
	for(it = mTracks.begin(); it!=mTracks.end(); it++)
	{
		if(!(*it)->sub->readSource()->isCurrentlyAwaitingData())
		{
			(*it)->sub->readSource()->getNextFrame((*it)->buffer, (*it)->sz_buffer,
				StreamRead, *it, StreamClose, *it);
		}
	}

	TaskToken      task;

	mEventFlag = 0;

    /* Create a task that will be called if we wait more than 300ms */
    task = mSched->scheduleDelayedTask( 5 * 1000, TaskInterruptData, this );

    /* Do the read */
    mSched->doEventLoop( &mEventFlag );

    if(-1 != mEventFlag)
    {
    	/* remove the task */
    	mSched->unscheduleDelayedTask( task );
    }

	return 0;
}

CRTSPClient::~CRTSPClient()
{
	_reset();
	if(mTaskKeepAlive)
	{
		mSched->unscheduleDelayedTask(mTaskKeepAlive);
		mTaskKeepAlive=NULL;
	}

	Medium::close(mMs);
	RTSPClient::close(mMyClient);

	std::list<CTrack*>::iterator it;
	for(it = mTracks.begin(); it != mTracks.end(); it++)
	{
		delete[] (*it)->buffer;
		delete *it;
	}

	mEnv->reclaim();///fixme: delete _env????
	SCT_DELETE(mSched);

	SCT_DELETE(mObjMutex);
}

void CRTSPClient::pushH264Data(const int64_t & pts, uint8_t*d, const uint32_t & len, const uint32_t & dropFramePre)
{
	uint32_t nOut = len+4;
	uint8_t * base = new uint8_t[nOut];

	uint8_t * p = base;

	*p = 0x0;
	p++;

	*p = 0x0;
	p++;

	*p = 0x0;
	p++;

	*p = 0x1;
	p++;

	memcpy(p, d, len);

	CBlockInfo info;
	info.type = CRTSPServerInterface::H264;
	info.pts = pts;
	info.drop_frame_pre = dropFramePre;

	mCallback->onRecvData(this, base, nOut, info);

	SCT_DELETE_ARRAY(base);
}

#define MPEGTS_PACKET_SIZE		188
void CRTSPClient::pushTsData(const int64_t & pts, uint8_t*d, const uint32_t & len, const uint32_t & dropFramePre)
{

	///parse the ts
	uint8_t* p = d;
	int left= len;

	while(left > 0)
	{
		if(mCallback)
		{
			CBlockInfo info;
			info.type = CRTSPServerInterface::TS;
			info.pts = pts;
			info.drop_frame_pre = dropFramePre;

			mCallback->onRecvData(this, p, MPEGTS_PACKET_SIZE, info);
		}

		p +=  MPEGTS_PACKET_SIZE;
		left -= MPEGTS_PACKET_SIZE;
	}
}

struct _ADTS_Header
{
	uint8_t syncword_h;

	uint8_t protection_absent:1;
	uint8_t layer:2;
	uint8_t id:1;
	uint8_t syncword_l:4;

	uint8_t channel_configuration_h:1;
	uint8_t private_bit:1;
	uint8_t sampling_frequency_index:4;
	uint8_t profile:2;

	uint8_t aac_frame_length_h:2;
	uint8_t copyright_identification_start:1;
	uint8_t copyright_identification_bit:1;
	uint8_t home:1;
	uint8_t original_copy:1;
	uint8_t channel_configuration_l:2;

	uint8_t aac_frame_length_m;

	uint8_t adts_buffer_fullness_h:5;
	uint8_t aac_frame_length_l:3;

	uint8_t number_of_raw_data_blocks_in_frame:2;
	uint8_t adts_buffer_fullness_l:6;
};

static int const samplingFrequencyTable[16] = {
  96000, 88200, 64000, 48000,
  44100, 32000, 24000, 22050,
  16000, 12000, 11025, 8000,
  7350, 0, 0, 0
};

static int getIndexFromFreq(int f)
{
	int sz = sizeof(samplingFrequencyTable)/sizeof(samplingFrequencyTable[0]);

	for(int i = 0; i<sz; i++)
	{
		if(samplingFrequencyTable[i] == f)
		{
			return i;
		}
	}

	return -1;
}

void CRTSPClient::pushAACData(CRTSPClient::CTrack* track, const int64_t & pts, uint8_t*d, const uint32_t & len, const uint32_t & dropFramePre)
{
	uint32_t nOut = len+sizeof(_ADTS_Header);
	uint8_t * base = new uint8_t[nOut];

	uint8_t * p = base;

	_ADTS_Header h;

	h.syncword_h = 0xff;
	h.syncword_l = 0xf;

	h.id = 0;
	h.layer = 0;
	h.protection_absent = 1;

	h.profile = track->av_param.profile;
	h.sampling_frequency_index = getIndexFromFreq(track->av_param.frequency);
	h.private_bit = 0;
	h.channel_configuration_h = (track->av_param.channel_config&0x4)>>2;
	h.channel_configuration_l = track->av_param.channel_config&0x3;

	h.original_copy = 0;
	h.home = 0;
	h.copyright_identification_bit = 0;
	h.copyright_identification_start = 0;

	int aac_len = len+7;
	h.aac_frame_length_h = (aac_len&0x1800)>>11;
	h.aac_frame_length_m = (aac_len&0x7f8)>>3;
	h.aac_frame_length_l = aac_len&0x7;

	int aac_buffer_fullness = 0x7ff;
	h.adts_buffer_fullness_h = (aac_buffer_fullness&0x7c0)>>6;
	h.adts_buffer_fullness_l = aac_buffer_fullness&0x3f;

	h.number_of_raw_data_blocks_in_frame = 0;

	memcpy(p, &h, sizeof(h));
	p += sizeof(h);

	memcpy(p, d, len);

	CBlockInfo info;
	info.type = CRTSPServerInterface::AAC;
	info.drop_frame_pre = 0;
	info.pts = pts;

	mCallback->onRecvData(this, base, nOut, info);

	SCT_DELETE_ARRAY(base);
}

void CRTSPClient::pushAMRData(const int64_t & pts, uint8_t*d, const uint32_t & len, const uint32_t & dropFramePre)
{
	int nOut = len+1;
	uint8_t* base = new uint8_t[nOut];

	uint8_t* p = base;

	*p = 0x3c;
	p++;

	memcpy(p, d, len);

	CBlockInfo info;
	info.drop_frame_pre = 0;
	info.pts = pts;
	info.type = CRTSPServerInterface::AMR;

	mCallback->onRecvData(this, base, nOut, info);

	SCT_DELETE_ARRAY(base);
}

}
